﻿#Requires AutoHotkey v2.0
#Include ../cfg/theme.ahk
#Include ../utils/path.ahk
#Include ../bin/handler.ahk
#Include ../bin/re.ahk

class PlanEGui extends Gui {
  __New(theme, title := '', opt := '') {
    super.__New('-Caption +Border +AlwaysOnTop' opt, title, this)
    this.SetFont('s16', 'Consolas')
    ; set the theme
    theme = 'light'
      ? this.currTheme := Light()
      : this.currTheme := Dark()
    this.BackColor := this.currTheme.window_Bgc
    ;
    this.AddButton('w0 h0 ym Default').OnEvent('click', 'Handle')
    this.AddText('xP-5 yp w10 vMark c' this.currTheme.edit_Fc, '')
    this.AddEdit('yp w300 h30 -Multi vSendBox '
      'c' this.currTheme.default_Fc
      ' Background' this.currTheme.edit_Bgc, '')
    (this.currTheme is Dark)
    ? this.AddPicture('yp h30 w50 vPic', Path.Resolve(Path.Workdir, 'res/light_3.png'))
    : this.AddPicture('yp h30 w50 vPic', Path.Resolve(Path.Workdir, 'res/light_2.png'))
    this['Pic'].OnEvent('Click', 'MoveWin')
    this['Pic'].OnEvent('DoubleClick', 'Exit')
    this.fontPixel := 23
    this.deadMsg := 30
  }

  AnimateHide(*) {
    ; add an animation to the hidden window, because the default style is missing when set the option '-Caption'
    ; and, simulate the mouse click up event
    this['MiniBtn'].GetPos(&wx, &wy, &ww, &wh)
    while GetKeyState("LButton", "P") {
      Sleep(10) ; necessary
      if !GetKeyState('lButton', 'p') {
        CoordMode 'Mouse', 'Window'
        MouseGetPos(&nx, &ny)
        ; whether the mouse cursor hover on the minimize button
        if nx > (wx - 10) and nx < (wx + ww + 10) and ny > (wy - 10) and ny < (wy + wh + 10) {
          this.GetClientPos(&cx, &cy, &cw, &ch)
          clientW := cw, clientH := ch
          while cw > 0 {
            ; change the hide style via modify the cw to ch, or both cw & ch
            this.Move(, , cw--,)
          }
          WinSetTransparent(0, 'ahk_id' this.Hwnd)
          this.Move(cx, cy, clientW, clientH)
          this.Minimize()
          WinSetTransparent(255, 'ahk_id' this.Hwnd)
        }
      }
    }
  }

  MoveWin(*) {
    ; move gui window via dragging the header bar
    CoordMode "Mouse", "Screen"
    MouseGetPos(&px, &py)
    WinGetPos(&wx, &wy, , , 'ahk_id' this.Hwnd)
    dx := wx - px, dy := wy - py
    SetWinDelay -1
    While GetKeyState("LButton", "P")
    {
      MouseGetPos(&nx, &ny)
      WinMove(nx + dx, ny + dy, , , 'ahk_id' this.Hwnd)
    }
  }

  static Show(config, *) {
    ; singleton
    for w in WinGetList('ahk_pid ' ProcessExist())
      if (g := GuiFromHwnd(w)) && g.base = this.Prototype
        return g.Show()
    ins := PlanEGui(config.theme)
    ins.Show('Minimize')
    WinSetTransparent(0, 'ahk_id' ins.Hwnd)
    ins.Restore()
    ins.GetClientPos(, , , &ch)
    ins.Move(, A_ScreenHeight / 4, , 0)
    WinSetTransparent(255, 'ahk_id' ins.Hwnd)
    loop ch {
      ins.Move(, , , A_Index)
    }
    ins.AddText('xm+5 h800 w400 vHistory c' ins.currTheme.edit_Fc,)
    ins['History'].SetFont('s12')
    ControlFocus ins['SendBox'].Hwnd, "A"
  }

  static Hide() {
    for w in WinGetList('ahk_pid ' ProcessExist())
      if (g := GuiFromHwnd(w)) && g.base = this.Prototype
        g.Hide()
  }

  Exit(*) {
    this.GetClientPos(, , , &ch)
    while ch > 0 {
      this.Move(, , , ch--)
    }
    ExitApp
  }

  Handle(*) {
    cmd := this['SendBox'].Text
    if !cmd {
      ; null input
      this.FitSize(this.fontPixel)
      this.AddHistory(false, '')
      return
    }
    ; check vaild
    ret := Re.CheckVaild(cmd)
    static h := Handler()
    ; vaild
    if ret.flag {
      ; echo input
      this.FitSize(this.fontPixel)
      this.AddHistory(true, ret.value)
      ; get handled result
      result := h.Handle(cmd, ret.value, ret.attach)
      ; set flag
      this.SetFlag(result.special)
      ; echo result
      this.FitSize(this.fontPixel * result.lineNum)
      this.AddHistory(false, result.msg)
    }
    ; failure
    else {
      this.FitSize(this.fontPixel)
      ; Cut too long command
      if StrLen(cmd) >= 15
        cmd := SubStr(cmd, 1, 20) . '...'
      this.AddHistory(false, ret.value '--' cmd)
    }
    this.Clear()
  }

  SetFlag(mark) {
    this['mark'].Value := mark
  }

  Clear() {
    this['SendBox'].Text := ''
  }

  FitSize(pixel, operSub := false) {
    this.GetClientPos(, , , &ch)
    if operSub { ; sub
      if ch - pixel <= 50
        pixel := ch - 55
      loop pixel {
        this.Move(, , , ch - A_Index)
      }
    }
    else {
      loop pixel {
        this.Move(, , , ch + A_Index)
      }
    }
  }

  AddHistory(isInput, text) {
    prompt := isInput ? '<< ' : '>> '
    this['History'].Value .= prompt text '`n'
    this.AutoClearHistory()
  }

  AutoClearHistory() {
    static count := IsSet(count) || 0
    count++
    history := this['History'].Value
    static newHis := SubStr(history, InStr(history, '`n') + 1,)
    if count >= this.deadMsg {
      this['History'].Value := newHis
      sleep 300
      this.FitSize(this.fontPixel * count, true)
      count := 0
    }
  }
}

if A_LineFile == A_ScriptFullPath {
  Path.Workdir := Path.Resolve(A_ScriptDir, '../')
  PlanEGui.Show({ theme: 'light' })
}